package com.lry.jvm.instructions.refrences;

import com.lry.jvm.instructions.base.Index16Instruction;
import com.lry.jvm.instructions.base.MethodInvoke;
import com.lry.jvm.rtda.Frame;
import com.lry.jvm.rtda.JObject;
import com.lry.jvm.rtda.heap.*;
import com.lry.jvm.util.Util;
//<init>,super,实例方法
public class invokespecial extends Index16Instruction {
    @Override
    public byte getOpCode() {
        return (byte) 0xb7;
    }

    @Override
    public void execute(Frame frame) {
        //frame.getOperandStack().popRef();
        //调用的时候 当前类
        JClass curClass = frame.getMethod().getJClass();
        RuntimeConstantPool rcp = curClass.getConstantPool();
        MethodRef methodRef = (MethodRef) rcp.getConstant(index);

        //被调用的方法所属的类
        JClass resolvedClass = methodRef.resolvedClass();
        //被调用的方法
        JMethod resolvedMethod = methodRef.resolvedMethod();

        if (resolvedMethod.getName().equals("<init>") && resolvedMethod.getJClass() != resolvedClass) {
            Util.panic("java.lang.NoSuchMethodError");
        }
        if (resolvedMethod.isStatic()) {
            Util.panic("java.lang.IncompatibleClassChangeError");
        }
        //拿出this
        JObject ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgsSlotCount() - 1);
        if (ref == null) {
            Util.panic("java.lang.NullPointerException");
        }

        //protected方法只能被子类或当前类调用
        if(resolvedMethod.isProtected()&&
        curClass.isSubClassOf(resolvedMethod.getJClass())&&
        !resolvedMethod.getJClass().getPackageName().equals(curClass.getPackageName())&&
        ref.getJClass()!=curClass&&
        ref.getJClass().isSubClassOf(curClass)){
            Util.panic("java.lang.IllegalAccessError");
        }

        //super调用父类方法
        if(curClass.isSuper()&&
        curClass.isSubClassOf(resolvedClass)&&
        !resolvedMethod.equals("<init>")){
            resolvedMethod = MethodLookup.lookupMethodInClass(curClass.getSuperClass(),methodRef.getName(),methodRef.getDescriptor());
        }

        if(null==resolvedMethod || resolvedMethod.getJClass().isAbstract()){
            Util.panic("java.lang.AbstractMethodError");
        }

        MethodInvoke.invokeMethod(frame,resolvedMethod);
    }
}
